Skip to content

fix(spec,lint,metadata-protocol): correct the view container name ledger note; delete the unreachable list-view tabs walks - #21423

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20301-view-container-name-retired
Oct 2, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-20301-view-container-name-retired

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #20301
Clause-②: no

Stage 2 of the card, re-scoped by claim amendment 5953063959 and its correction 5953104466. Stage 1 (PR #20357) retired list.tabs. Stage 2 does not retire the view container's body name, because the A2 stop fired: the metadata door writes that key itself (os-dev report 5953000865). What lands here is the corrected ledger record plus the carried notes from the stage-1 landing record 5870707479.

⛔ No tombstone, no conversion, no door change, no view.zod.ts key change. #21412 (the runtime door accepts a contradicting container name) is not addressed here and remains open.

What changed

1. packages/spec/liveness/view.json, the name row: the status stays dead and only the note is rewritten. verifiedAt is now 2026-10-02.

  • Why the verdict holds. The ledger defines live as "authoring the property changes runtime behaviour". An authored container name either restates the key the container already registers under or contradicts it. So os validate / os lint keep their liveness-dead-property warning ("drop it"), which is still the right advice.
  • What the note corrects. It no longer says "a copy nobody reads", because the door writes and reads that copy. saveMetaItem runs normalizeViewMetadata ahead of the schema gate, and it stamps the save name onto every view body that has none, containers included. This is pinned by view-container-runtime-expansion.test.ts. The overlay paths then key on the stamped copy: hydrateOverlayIntoRegistry registers no body without a name, and mergePackageAwareOverlay slots a row by it. The ObjectQL boot loop also mints the derived key onto every stack container it registers.
  • Why the key is kept, not retired. A tombstone would refuse the platform's own saves. The 2026-09-03 ruling (PR fix(objectql): the boot loop refuses a view container whose name disagrees with its derived object key (#14666) #15319) refused direction 3. Triage's guard on this card forbids retiring a key the platform's own writer still sends. The note follows the ledger's "kept deliberately" precedent.
  • The old attribution, corrected. The note used to say artifact-shipped containers and the metadata-validation sweep send the key. They do not: what was read as theirs is the door's stamp.
  • The ledger README's view cell carried the same false sentence and is corrected the same way. The view.list.tabs row's note now records that the two walks below are deleted.
  • Counts. gen:liveness-counts printed 0 shard(s) rewritten, 0 pruned, and the totals are unchanged: 988 live · 3 experimental · 1 live-elsewhere · 109 dead · 10 planned = 1111.

2. The carried notes, at their re-measured locations.

  • packages/spec/src/system/i18n-resolver.ts: a comment still called ListViewSchema.tabs a live carrier. It now names the tombstone and says UserFiltersSchema.tabs is the one carrier. This is a comment-only change.
  • packages/lint/src/validate-list-view-field-refs.ts: the checkTabs(listView.tabs, …) call is deleted. This rule is input: 'parsed' in the authoring-rule registry, and every list-view shape tombstones tabs, so the key could never reach the call. The userFilters.tabs walk stays. Header prose that named tabs[].filter / tabs[].view as walked positions is updated.
  • packages/metadata-protocol/src/metadata-diagnostics.ts: the view?.tabs read in computeViewReferenceDiagnostics is deleted. The write door refuses the key, and stored and artifact bodies have it stripped by the conversion replay (applyConversionsToStoredItem / applyArtifactForwardConversions) before they are served. A body that still carries it is badged by computeMetadataDiagnostics with the tombstone prescription.
  • Fixtures. The list-tabs fixtures in packages/lint/src/validate-list-view-field-refs.test.ts and packages/objectql/src/metadata-diagnostics.test.ts are deleted. The objectql case that asserted the deleted read now asserts the surviving userFilters.tabs read on the same unknown field. HARD_CODED_FILTER_WALKS drops tabs.filter.field. The stage-1 tree-scoped absence pin (view-list-tabs-retirement.test.ts) drops those two files from its self-expiring RESIDUE, as that set's own assertion requires. The CLI i18n entry stays.

3. Patch changesets: @objectstack/spec (liveness/ is in its files[]), @objectstack/lint and @objectstack/metadata-protocol. Each carries Clause-②: no. objectql changes only a test file, and its files[] ships dist only, so it gets no changeset.

Verification

HEAD 7ee481ef2d (base 6d67ad5eca). Every os-verify-lock run below reports VERDICT command-exit 0.

Command Result
pnpm --filter '@objectstack/objectql^...' build spec, lint, metadata-protocol and the closure built, with declarations
pnpm --filter @objectstack/spec check:generated ✓ All 15 generated artifacts are up to date
pnpm --filter @objectstack/spec typecheck exit 0
pnpm --filter @objectstack/spec check:liveness exit 0, ✓ packages/spec/liveness/state-counts/ is current
pnpm --filter @objectstack/spec test Test Files 600 passed (600), Tests 17601 passed / 1 todo
pnpm --filter @objectstack/lint test && … typecheck Test Files 119 passed, Tests 5574 passed, test-typecheck OK
pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 && … typecheck Test Files 201 passed / 3 skipped, Tests 2983 passed / 19 skipped
objectql: three test files, typecheck, build metadata-diagnostics, view-container-divergent-name-registrars, metadata-validation-sweep: Tests 23 passed; typecheck OK

Derived gates. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands printed 91 commands. Each was run, and its exit code was recorded before any pipe. --ran printed ✓ dispatch-gates --ran: 91 derived famil(ies) accounted for — 90 run, 1 NOT-MEASURED. That includes check:adr-0087-registration (✓ … no declared-breaking changeset (3 non-breaking changeset(s) seen)), check-changeset-no-major (✓ This diff introduces no major bump), check-empty-changeset, check:changeset-gate-self-tests, check:doc-authoring, check:nul-bytes, check:cross-package-test-inputs, and every @objectstack/spec check:* the derivation named.

Two gates did not measure anything locally:

  • NOT MEASURED: check:dual-build-cjs-loads. It exited 3 (PREREQUISITE NOT MET) because it needs every package built, which this run did not do.
  • NOT MEASURED: check-engine-split-ratio --days 90. It exited 2 with cannot compute … this clone is shallow. It is an ADR trigger metric over git history, and it is recorded as not measured rather than as a pass.

check:lean-entry-closure first exited 3 for lack of objectql's dist. After objectql was built it exited 0 (✓ … Admitted set held exactly).

Narrowing, declared: the branch is not merged with origin/main. ceb4a939b4 is 7 commits ahead, and git diff --stat 6d67ad5eca ceb4a939b4 over this PR's 11 paths is empty. CI's merge ref judges the combination.

Acceptance notes

  • objectui's tabs?: ListViewSchema['tabs'] mirror (types objectql.ts) belongs to objectui. It picks up the stage-1 tombstone at its next pin bump, and this PR does not edit it.
  • packages/spec/src/ui/view.zod.ts (the ViewSchema guidance comment, about lines 4720-4728) still says artifact-shipped containers and the validation sweep send the container name. That is the same misattribution the ledger note corrects. The comment is left alone, per this stage's no-view.zod.ts scope. Carrier: the next PR to touch that block, or none.
  • skills/**: no hits for the touched surfaces.
  • The at-tier contract review follows this PR (seat's note on the claim amendment).

Generated by Claude Code

claude added 2 commits October 2, 2026 13:17
…ger note; delete the unreachable list-view tabs walks

The liveness row for the view container's body `name` stays `dead`, but its
note claimed "a copy nobody reads". Measured, the metadata door stamps the
save name into every saved view body (`normalizeViewMetadata`) and its
overlay paths key on that copy (`hydrateOverlayIntoRegistry`,
`mergePackageAwareOverlay`). The note and the ledger README now say so, and
record why the key is kept rather than retired.

The list view's own `tabs` is a tombstone on every list-view shape, so the
two author-time walks that still read it (the lint list-view field-ref rule,
`computeViewReferenceDiagnostics`) could never see it: the lint judges the
parsed stack, and every view door refuses or strips the key. Both reads, and
their fixtures, are deleted; each keeps its `userFilters.tabs` walk. The
stale i18n-resolver comment that still called the list carrier live is
corrected.

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
… the deleted tabs walks

Claude-Session: https://claude.ai/code/session_01YDt3PzwfrkuFzUBF89WPmM
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

2 anchor(s) derived from 3 changed package(s); no hand-written page names any of them. ⚠️ 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/view.json, packages/spec/src/system/i18n-resolver.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/spec/liveness/README.md, packages/spec/liveness/view.json, packages/spec/src/system/i18n-resolver.ts) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 139 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 28e65c82c6bb56932e3f040e0e35a6b5a66fdd13 — the merge of head 7ee481ef2d32a880bc533f9f02b0eadaeeea1e92 into base ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 28e65c82c6bb56932e3f040e0e35a6b5a66fdd13 && git checkout 28e65c82c6bb56932e3f040e0e35a6b5a66fdd13
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc 7ee481ef2d32a880bc533f9f02b0eadaeeea1e92 && git checkout -B drift-repro ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc && git merge --no-ff 7ee481ef2d32a880bc533f9f02b0eadaeeea1e92

node scripts/docs-audit/affected-docs.mjs --json ceb4a939b42f7c91ac8e9d371df34b8663d3f7bc

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 7ee481ef2d32a880bc533f9f02b0eadaeeea1e92
Local-runs: none

Read at 2026-10-02T14:42Z by an isolated subagent of the seat session, against the card (#20301, body and all 24 comments), the PR body and file list (11 files, +88/−59), the net diff against main, and the head's check-runs. The scope of record is the re-scope 5953063959 as corrected by 5953104466. Every file claim below was re-read at the head with git show; nothing was built, run or re-run.

① Derived judgments

Accept set at every metadata door: unchanged — right. No view.zod.ts, no conversion, no tombstone, no door change in the file list. The two code deletions remove reads of a key the spec already refuses:

  1. @objectstack/lint validateListViewFieldRefs — checkTabs(listView.tabs, …) deleted. Right. The rule enters AUTHORING_RULES through the validateReferenceIntegrity row (authoring-rules.ts:880, tier: 'gating', input: 'parsed', runtimeTypes includes view), and tabs is retiredKey( on ListViewShapeSchema (view.zod.ts:2886), the shape ListViewSchema, ObjectListViewSchema and the flattened overlay arm are built from — so on the parsed stack the key never reaches the walk. At the runtime write door the schema gate (resolveOverlaySchema().safeParse) refuses the key with its prescription; the deleted walk could at most have added a list-view-field-unknown finding under a key already refused. checkTabs stays defined and keeps its one caller, userFilters.tabs (:801). The header prose that listed tabs[].filter and tabs[].view as walked positions is corrected, and the test's exact-list HARD_CODED_FILTER_WALKS drops tabs.filter.field as that assertion requires.
  2. @objectstack/metadata-protocol computeViewReferenceDiagnostics — the view?.tabs read deleted. Right. Its one production caller is getMetaItem (protocol.ts:9028), on the decorated served body: decorateMetadataItem runs computeMetadataDiagnostics, the schema re-parse that badges a tombstoned key with its prescription; a stored row replays the full chain including retired entries (applyConversionsToStoredItem), and an artifact body goes through applyArtifactForwardConversions with includeRetired: true (artifact-forward-conversion.ts:446), so the D2 view-list-tabs-removed strips tabs before the walk. The objectql test case is converted to the surviving userFilters.tabs read on the same unknown field — a negative case that file lacked — right.
  3. packages/spec/liveness/view.json name row — status stays dead, note rewritten, verifiedAt moved to 2026-10-02. Right, and it is the published surface this PR changes (liveness is in @objectstack/spec files[]). Each claim in the note holds at the head: normalizeViewMetadata (protocol.ts:1220, name: saveName when the body has none) is called in saveMetaItem at :17256, ahead of the schema parse at :17361; hydrateOverlayIntoRegistry (:15962) returns false for a body without name (:15990); mergePackageAwareOverlay (:1808) skips a body without name and slots by body.name; registerMetadataCollections mints name: itemName (engine.ts:7010); the cited pin is view-container-runtime-expansion.test.ts:252 ({ ...leadContainer, name: 'crm_lead' }, "the write door has always stamped" at :248); the objectui pin at the head is 89cad75d55, as the note says. dead under the ledger README's definition ("authoring the property changes runtime behaviour") is the reading the correction 5953104466 ordered; no status moves, so state-counts is unchanged (Spec property liveness: success). The runtime-door divergence the note names is filed as finding(metadata-protocol): the runtime save door accepts a view container whose body name contradicts its row name and registers it under both keys; the two source registrars refuse the same document #21412 (pointer 5953132688).
  4. liveness/README.md view cell and the list.tabs note — the same correction, and the record that the two walks were deleted. Right: the retirement skill's ledger discipline requires the README row to follow a ledger correction, and the only other site in the tree carrying the old attribution is the view.zod.ts comment flagged under ③.
  5. i18n-resolver.ts comment — comment-only; its @objectstack/spec 17.5.0 cite is right (the stage-1 merge 6e3e5462c6 is listed under ## 17.5.0 in the spec CHANGELOG; the merge sat at 17.4.0 in package.json).
  6. view-list-tabs-retirement.test.ts RESIDUE — dropping the two fixture files is forced by the set's own assertion (:559-560: every entry must still hold an offender). The fixtures that remain (userFilters: { fields, tabs }, userFilters: { element, tabs }) are not offenders under isOffendingKeySet (needs tabs beside a LIST_VIEW_SIBLINGS key in the same frame). Right.
  7. Governed surfaces: none in the file list (no .claude/**, skills/**, docs/adr/**, AGENTS.md, CLAUDE.md, NORTH-STAR.md); Governed Surface Queue Guard: success. This record is owed by the dispatch's tier ceiling, not by Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14.
  8. Scope of record: items 1–3 of 5953063959 as corrected by 5953104466 are each delivered; the prohibited acts (tombstone, conversion, door change, view.zod.ts key change) are absent; Fixes #20301 is the closing the correction kept. The card this PR closes must claim this branch and Part-of PR must not also close its card: both success.
  9. Base: the PR's merge base is 6d67ad5eca; origin/main (22c2d6f4d5 at this read) has not moved on any of the 11 paths since it (git diff --stat over those paths is empty), so the declared non-merge carries no path risk here and CI's merge ref judges the rest.

Gate verdicts on the head, read at 2026-10-02T14:40Z: 24 success · 3 skipped (Build Docs, Console Pin Gate, Packed-tarball smoke (opt-in)) · 7 in progress (Lint & Repo Gates, Test Core 1/3/4/5/6, TypeScript Type Check). Success includes Build Core, Check Changeset, Spec property liveness, Governed Surface Queue Guard, Type Check · source gates / consumer gates / debt ledger, Dogfood Regression Gate 1–3, Dogfood Verify CLI, Temporal Conformance, Test Core (2/6). An in-progress gate is an honest reading, not a pass: landing waits for the seven to finish green.

② Semver level

Right: three patch changesets, each with the line Clause-②: no, and no skip-changeset. @objectstack/spec ships liveness/ in files[], so the ledger note is a published change and skip-changeset would be wrong; @objectstack/lint and @objectstack/metadata-protocol change code whose behaviour is preserved on every input a door accepts. @objectstack/objectql changes one test file and its files[] is dist/README.md/CHANGELOG.md, so no changeset is owed. The no arm is right with no (narrowing)/(widening): nothing an author can write is newly accepted or newly refused, and no export is added or removed. No breaking changeset, so no ADR-0087 marker is owed; the dev's local check:adr-0087-registration and check-changeset-no-major read ✓, and Check Changeset on the head is success.

③ Boundary flags

open_questions: none. Deviations and findings in the dev's report 5954648905, each answered:

  • README cell and list.tabs note corrected beside the name note — in scope: item 1 is "correct the ledger", and the README row follows a ledger correction by the retirement skill's rule. Answered.
  • Lint header prose and test comments updated to the deletion — part of item 2. Answered.
  • objectql case converted, not deleted — right; it is the surviving walk's only negative case in that file. Answered.
  • Not merged with origin/main — measured above (⑨): no overlap on the 11 paths. Answered, no action.
  • Commit trailers: the model-free pair — AGENTS.md's "Commit message" rule names exactly that pair; both commits carry it and nothing else. Answered, right.
  • Labels; worktree removed — not contract matters. Answered.
  • view.zod.ts lines 4720–4728 still say artifact-shipped containers and the validation sweep send the container name (out-of-scope finding, carrier "the next PR to touch that block, or none"). Verified at the head (:4722-4723): it is the one remaining site of the misattribution this PR corrects. Answered in part: the scope of record forbade a view.zod.ts KEY change, not a comment correction, so the fix was permissible but not ordered, and the comment's conclusion ("tombstoning them rejected shapes the platform itself writes") stays true with its first clause (saveMetaItem sends the name) the true one. Not blocking: no published surface, no accept-set effect, and the record of record (ledger row and README) now carries the correction and names the old attribution as a misread. Escalated to the seat: "or none" is not a carrier; name one — a comment-only follow-up on that block, or the finding(metadata-protocol): the runtime save door accepts a view container whose body name contradicts its row name and registers it under both keys; the two source registrars refuse the same document #21412 landing, which re-reads the container-name semantics.
  • objectui tabs?: ListViewSchema['tabs'] mirror — carried from 5870707479 to the next pin bump; the pin is unchanged by this PR. Answered.

Implemented-by: claude/issue-20301-view-container-name-retired
Reviewed-by: session_01YDt3PzwfrkuFzUBF89WPmM

VERDICT: PASS


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(ui): retire list.tabs and the view container's body name (2 keys); listViews + ViewTabBar and the row name already deliver both

2 participants